/**********************************************************************
 Copyright (C) 2011 Jaakko Vuori

 This file is part of ShredMate.
 For more information, see <http://code.google.com/p/shredmate/>

 ShredMate is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 ShredMate is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with ShredMate.  If not, see <http://www.gnu.org/licenses/>.
 **********************************************************************/

#include "mp3pcmbuffer.h"

#include <mpg123.h>
#include <stdio.h>
#include <QCryptographicHash>

void cleanup(mpg123_handle *mh)
{
    mpg123_close(mh);
    mpg123_delete(mh);
    mpg123_exit();
}

MP3PCMBuffer::MP3PCMBuffer(QString fileName) :
    m_fileName(fileName), m_channels(-1), m_bitRate(-1)
{
}

void MP3PCMBuffer::open()
{
    m_decodedData.clear();

    mpg123_handle* mh = NULL;
    unsigned char* buffer = NULL;
    size_t buffer_size = 0;
    size_t done = 0;
    int encoding = 0;
    int err = MPG123_OK;

    err = mpg123_init();
    if (err != MPG123_OK || (mh = mpg123_new(NULL, &err)) == NULL
            /* Let mpg123 work with the file, that excludes MPG123_NEED_MORE messages. */
            || mpg123_open(mh, m_fileName.toAscii()) != MPG123_OK
            /* Peek into track and get first output format. */
            || mpg123_getformat(mh, &m_bitRate, &m_channels, &encoding) != MPG123_OK)
    {
        fprintf(stderr, "Trouble with mpg123: %s\n",
            mh == NULL ? mpg123_plain_strerror(err) : mpg123_strerror(mh));
        cleanup(mh);
        throw;
    }

    if (encoding != MPG123_ENC_SIGNED_16)
    { /* Signed 16 is the default output format anyways; it would actually by only different if we forced it.
             So this check is here just for this explanation. */
        cleanup(mh);
        fprintf(stderr, "Bad encoding: 0x%x!\n", encoding);
        throw;
    }
    /* Ensure that this output format will not change (it could, when we allow it). */
    mpg123_format_none(mh);
    mpg123_format(mh, m_bitRate, m_channels, encoding);

    /* Buffer could be almost any size here, mpg123_outblock() is just some recommendation.
           Important, especially for sndfile writing, is that the size is a multiple of sample size. */
    buffer_size = mpg123_outblock(mh);
    buffer = (unsigned char *) malloc(buffer_size);

    do
    {
        err = mpg123_read(mh, buffer, buffer_size, &done);
        m_decodedData.append((const char*)buffer, done);
    } while (err == MPG123_OK);

    m_hash = QCryptographicHash::hash(m_decodedData, QCryptographicHash::Md5);

    if (err != MPG123_DONE)
        fprintf(stderr, "Warning: Decoding ended prematurely because: %s\n",
            err == MPG123_ERR ? mpg123_strerror(mh) : mpg123_plain_strerror(err));

    cleanup(mh);
}

int MP3PCMBuffer::channels() const
{
    return m_channels;
}

int MP3PCMBuffer::bitRate() const
{
    return m_bitRate;
}

qint64 MP3PCMBuffer::length() const
{
    return m_decodedData.size();
}

const char* MP3PCMBuffer::data() const
{
    return m_decodedData.constData();
}

QByteArray MP3PCMBuffer::hash() const
{
    return m_hash;
}
